/**
 * Copyright (C) 2011-20077
 * 设计：		Zhangsk
 * 创建时间：	2011-11-06
 * Flash视频会议核心库
 *
 * ---------------------------------------------------------
 * 2011-12-03 Zhangsk
 * 调整对协议栈的使用，连接IP，端口，会议号由上层应用设置。
 * 增加与服务器的交互，进入会议后下发视频打开信令
 * 
 * ---------------------------------------------------------
 * 2011-11-26 Zhangsk
 * 使用XML对象代替XMLNode对象分发事件  
 * XML对象可以使用XMLNode增加子节点
 * _xml.appendChild (xmlNode);
 * 
 * 注意：事件机制，接收方：接收事件的对象需要对其event进行赋值
 * 						trace1.event = session;
 *      发送方：都要使用session统一分发事件
 * TODO：ControlLoader 统一加载控制器，统一使用session发送事件
 * 			ui层对所有的组件统一进行event赋值
 */
package com.treemon.meetingcore.session
{
	import com.jivesoftware.spark.managers.ConnectionManager;
	import com.jivesoftware.spark.managers.SparkManager;
	import com.treemon.meetingcore.ExtObj.MessageObj;
	import com.treemon.meetingcore.event.MeetingEvent;
	import com.treemon.meetingcore.event.MeetingMessageEvent;
	import com.treemon.meetingcore.event.MeetingSocketEvent;
	import com.treemon.meetingcore.event.XMPPTraceEvent;
	import com.xlib.guid.GUID;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.system.Security;
	
	import mx.utils.StringUtil;
	
	import org.jivesoftware.xiff.core.XMPPConnection;
	import org.jivesoftware.xiff.data.Presence;
	import org.jivesoftware.xiff.events.ConnectionSuccessEvent;
	import org.jivesoftware.xiff.events.IQEvent;
	import org.jivesoftware.xiff.events.IncomingDataEvent;
	import org.jivesoftware.xiff.events.InviteEvent;
	import org.jivesoftware.xiff.events.OutgoingDataEvent;
	import org.jivesoftware.xiff.events.RosterEvent;
	import org.jivesoftware.xiff.events.StreamEvent;
	import org.jivesoftware.xiff.events.XIFFErrorEvent;
	import org.jivesoftware.xiff.util.*;
			
	//todo: server, port
	public class MeetingSession extends EventDispatcher
	{
		public function MeetingSession()
		{
			this.init();
		}
		
		public function free(): void
		{
			//todo: 通过信令告诉服务器，客户端要退出
			SparkManager.errorHandler = null;
			//XMPPConnection.disableSASLMechanism("ANONYMOUS");
			//_connectionManager = SparkManager.connectionManager;
 			var connection:XMPPConnection = _connectionManager.connection;
			connection.removeEventListener("login", handleConnected);
			connection.removeEventListener("disconnection", handleDisconnect);
			connection.removeEventListener("error", handleError);
			SparkManager.inviteListener.removeEventListener(InviteEvent.INVITED, handleInvited);
			
			//成功连接，客户端向服务器发送stream后
			connection.removeEventListener(ConnectionSuccessEvent.CONNECT_SUCCESS, handleConnectSuccess);
			
			connection.removeEventListener(StreamEvent.STREAM_STREAM, handleStreamStream);
				
			connection.removeEventListener(IncomingDataEvent.INCOMING_DATA, handleIncomingData);
			connection.removeEventListener(OutgoingDataEvent.OUTGOING_DATA, handleOutgoingData);

			connection.removeEventListener(IQEvent.INCOMING_IQ, handleIQEvent);
			connection.removeEventListener(IQEvent.INCOMING_Message, handleMessage);
			
			//_loginController.free();
			_loginController = null;
			//_videoController.free();
			_videoController = null;
			//_documentController.free();
			_documentController = null;
			_userManager = null;
			_audioController = null
			
		}

		/**
		 * 会议核心系统会话,登陆会议系统 
		 * @author 		Zhangsk
		 * @date		2011-11-26
		 * @param		aIP c2s的IP
		 * @param		aPort c2s的Port
		 * @return		void
		 * @throws	
		 * @see
		 * @eventType	com.treemon.meetingcore.event.MeetingEvent
		 * @eventType	com.treemon.meetingcore.event.XMPPTraceEvent
		 */
		public function login(aIP: String, aPort: int = 5223): void
		{
			_connectionManager.connection.port = aPort;
			//var strUrl: String = "http://"+aIP+"/web/crossdomain.xml";
			//trace(strUrl);
			//var strUrl: String = "http://127.0.0.1/crossdomain.xml";
			//Security.loadPolicyFile(strUrl);
			//todo 确定服务器的方式
			
			Security.loadPolicyFile("xmlsocket://"+aIP+":"+aPort.toString());
			Security.allowDomain( "*" );
			Security.allowInsecureDomain("*");
						
			_connectionManager.login("guest", "guest", aIP, "FlashMeetingClient");
		}
		
		/**
		 * 会议核心系统会话,发送XML数据 
		 * @author 		Zhangsk
		 * @date		2011-11-26
		 * @param		someData:*	建议参数为xml字符串
		 * @return		void
		 * @throws	
		 * @see
		 * @eventType	com.treemon.meetingcore.event.XMPPTraceEvent
		 */
		public function sendXML(someData: *): void
		{
			_connectionManager.connection.sendXML(someData);
		}
		
		/**
		 * 会议核心系统会话,发送Message数据 
		 * @author 		Zhangsk
		 * @date		2012-02-19
		 * @param		someData:*	建议参数为xml字符串
		 * @return		void
		 * @throws	
		 * @see
		 * @eventType	com.treemon.meetingcore.event.XMPPTraceEvent
		 */
		public function sendMessage(aMessage: MessageObj): void
		{
			_connectionManager.connection.sendXML(aMessage.toXMLString());
		}
		
		/**
		 * 会议核心系统会话,初始化 
		 * 引用连接管理器SparkManager.connectionManager
		 * 构造LoginControl
		 * @author 		Zhangsk
		 * @date		2011-11-26
		 * @param		void
		 * @return		void
		 * @throws	
		 * @see
		 * @eventType	
		 */
		private function init(): void
		{
			this.lastGUID = GUID.create();
			SparkManager.errorHandler = displayError;
			XMPPConnection.disableSASLMechanism("ANONYMOUS");
			_connectionManager = SparkManager.connectionManager;
 			var connection:XMPPConnection = _connectionManager.connection;
			connection.addEventListener("login", handleConnected);
			connection.addEventListener("disconnection", handleDisconnect);
			connection.addEventListener("error", handleError);
			SparkManager.inviteListener.addEventListener(InviteEvent.INVITED, handleInvited);
			
			//成功连接，客户端向服务器发送stream后
			connection.addEventListener(ConnectionSuccessEvent.CONNECT_SUCCESS, handleConnectSuccess);
			
			connection.addEventListener(StreamEvent.STREAM_STREAM, handleStreamStream);
				
			connection.addEventListener(IncomingDataEvent.INCOMING_DATA, handleIncomingData);
			
			connection.addEventListener(OutgoingDataEvent.OUTGOING_DATA, handleOutgoingData);
			connection.addEventListener(IQEvent.INCOMING_IQ, handleIQEvent);		
			connection.addEventListener(IQEvent.INCOMING_Message, handleMessage);
			
			_loginController = new LoginControl(this);
			_videoController = new VideoControl(this);
			_documentController = new DocumentControl(this);
			_userManager = new UserManager(this);
			_audioController = new AudioControl(this);
			
			
		}
		
		public function handleConnectSuccess(evt:ConnectionSuccessEvent):void 
		{
		//var so:SharedObject = SharedObject.getLocal("savedData");

 			trace('ConnectionSuccessEvent.CONNECT_SUCCESS');
		}
		
		public function handleIncomingData(evt:IncomingDataEvent): void
		{
			trace('IncomingDataEvent.INCOMING_DATA');
			var eventXMPPIncoming: XMPPTraceEvent = new XMPPTraceEvent(XMPPTraceEvent.XMPPIncoming);
			eventXMPPIncoming.data = evt.data.toString();
			dispatchEvent( eventXMPPIncoming );;
		}
		
		public function handleOutgoingData(evt:OutgoingDataEvent): void
		{
			trace('OutgoingDataEvent.OUTGOING_DATA');
			var eventXMPPOutgoing: XMPPTraceEvent = new XMPPTraceEvent(XMPPTraceEvent.XMPPOutgoing);
			eventXMPPOutgoing.data = evt.data;
			dispatchEvent( eventXMPPOutgoing );
		}
					
		public function displayError(name:String, message:String, fatal:Boolean):void
 		{
 			trace(name);
 			trace(message);
 			trace(fatal.toString());
 			if(fatal)
 				logout();
 		}
 			
 		private static function logout():void 
 		{
 			//todo logout
 			trace("logout");
			//ExternalInterface.call("logout");
		}
		
		private function handleConnected(event:Event):void 
 		{
 			SparkManager.roster.addEventListener(RosterEvent.ROSTER_LOADED, function():void { 
 			SparkManager.roster.groups; 
 				//show groups
 			});

			SparkManager.connectionManager.connection.send(new Presence());
 		}
 		
 		private function handleDisconnect(event:Event):void {
 			trace("handleDisconnect");
 			var evtSocket: MeetingSocketEvent = new MeetingSocketEvent(MeetingSocketEvent.MeetingSocketError);
			evtSocket.errorCondition = "";
			evtSocket.errorMessage = "Socket Disconnect";
			evtSocket.errorType = "";
			evtSocket.errorCode = 400;	//暂定400
			dispatchEvent( evtSocket );
 		}
 			
 		private function handleError(event:XIFFErrorEvent):void 
 		{
 			if(_suppressErrors) return;
 			trace(event.errorCode);
 			switch(event.errorCode) 
 			{
 				case 400:
 					trace("MeetingSession:: 400")
 					//loginDialog.reset();
 					//loginDialog.showErrorText(Localizator.getText('message.bad.request'));
 					break;
 				case 401:
 					trace("MeetingSession:: 401")
 					//沙箱冲突
 					var evtSocket: MeetingSocketEvent = new MeetingSocketEvent(MeetingSocketEvent.MeetingSocketError);
					evtSocket.errorCondition = event.errorCondition;
					evtSocket.errorMessage = event.errorMessage;
					evtSocket.errorType = event.errorType;
					evtSocket.errorCode = event.errorCode;
					dispatchEvent( evtSocket );
 					//loginDialog.reset();
 					//loginDialog.showErrorText(Localizator.getText('message.not.authorized'));
 					break;
 				case 501:
 					trace("MeetingSession:: 501")
 						//loginDialog.reset();
// 						if (event.errorMessage && event.errorCondition == "TLS required")
// 							loginDialog.showErrorText(Localizator.getText('message.tls.required'));
// 						else
// 							loginDialog.showErrorText(Localizator.getText('message.generic.error'));
 					break;
 				case 503:
 					trace("MeetingSession:: 503")
// 					//服务器不可用，网络未连接时直接报503
// 						loginDialog.reset();
// 						loginDialog.showErrorText(Localizator.getText('message.service.unavailable'));
 					break;
 			}
 		}
 			
 		private function handleInvited(event:InviteEvent):void 
        {
        	trace("handleInvited");
        	//Alert.show(event.from + event.room, event.reason);
        }
        
        private function handleStreamStream(evt: StreamEvent): void
        {
        	var s: String;        	
        	s = '<iq id="{0}" type="get"><query xmlns="jabber:iq:auth"><username>{1}</username></query></iq>';        	
        	sendXML( mx.utils.StringUtil.substitute(s, getGenerateID(), _meetingUserName) );
        }  
 		 
		public function testIQinfo(data:XML): void
		{
			var evt:IQEvent = new IQEvent(IQEvent.INCOMING_IQ);
			evt.xml=data;
			handleIQEvent(evt);			
		}
		
        /**
         * 会议核心系统会话,接收到iq信令的回调 
         * 构造LoginControl
         * @author      Zhangsk
         * @date        2011-11-26
         * @param       evt:IQEvent
         * @return      void
         * @throws  
         * @see
         * @eventType   com.treemon.meetingcore.event.MeetingEvent
         */ 		
        private function handleIQEvent(evt:IQEvent): void 
        {            	
            trace('IQEvent.INCOMING_IQ');
			var iq: XML = evt.xml;
			
			trace(iq.toXMLString());			
			trace(iq.children().length());
			if (iq.children().length() == 0)
			{
				var sType: String = iq.attribute("type");
				trace(sType);
				if (sType.toLowerCase() == "result")
				{
					//TODO 这个信令格式语意不明确；建议修改; 增加一个jiabber:iq:authok的事件
					var evtAuthOK: MeetingEvent = new MeetingEvent(MeetingEvent.AuthOK);	
					evtAuthOK.xml = iq;
					dispatchEvent( evtAuthOK );
				}
			}
			else
			{		
				var query: XML = iq.children()[0];	//TODO 查找字节点
				//todo 如果不是query节点抛出异常
				trace(query.name().localName);
				if ( (query != null) && (query.name().localName.toLowerCase() == "query") )
				{
					/**
					 * 
					 * query: XML = XML("<query xmlns='iq:xxx:xxx'>");
					 * query.name().localName	\\query
					 * query.name().uri			\\iq:xxx:xxx
					 * query.attribute("xmlns")	\\获取的值是""
					 * 
					 */
					 
					trace(query.name().localName ); //<query xmlns="iq:xxx:xxx"> node name
					trace(query.name().uri );		//xmlns属性值
					var evtMeetingEvent: MeetingEvent = new MeetingEvent(query.name().uri);
					trace("evtMeetingEvent.Create ok !");			
					evtMeetingEvent.xml = iq;					//目前给事件XML是完整的，以后可以考虑过滤iq内容是，尽量减少上层知道的信息，
					trace("evtMeetingEvent.xml ok !");
					dispatchEvent(evtMeetingEvent);
					trace("dispatchEvent(\"MeetingEvent\") ok !")
				}
			}//if (xmlNode.childNodes.length == 0)		

		}//handleIQEvent
		
		private function handleMessage(evt: IQEvent): void
		{
			trace('MessageEvent.handleMessage');
			var xmlMessage: XML = evt.xml;
			var msgFrom: String = xmlMessage.attribute("from");
			var nIndex: int = msgFrom.indexOf("/");
			msgFrom = msgFrom.substring(nIndex + 1, msgFrom.length);
			trace(msgFrom);
			
			trace(xmlMessage.toXMLString());	
			//var xmlQuery: XML = aEvent.xml.children()[0];
			var ns: Namespace = xmlMessage.namespace();
			var strMessage: String = xmlMessage.ns::body;
			trace(strMessage);
			var evtMessage: MeetingMessageEvent = new MeetingMessageEvent();
			
			evtMessage.message = strMessage;
			evtMessage.msgFrom = msgFrom;
			dispatchEvent( evtMessage );
		}
		
		/**
		* 返回iq信令中的id节点值，delphi中使用的jcl_1, flash里用fcl_1, 后面的数字是客户端发出信令的数量累计
		*/
		public function getGenerateID(): String
		{
			_iqCount++;
			return "fcl_" + _iqCount.toString();
		}
		
		public function addSpeaker(aMMID: String): void
		{
			var strRTMP: String = _fmsIP + aMMID + "_a";
			trace(strRTMP);
			_audioController.addSpeaker(strRTMP);
		}


		private var _connectionManager:ConnectionManager;
		private var _loginController: LoginControl;
		private var _videoController: VideoControl;		
		private var _documentController: DocumentControl;
		public var _userManager:UserManager;
		//private var _loaderManager: LoaderManager;
		private var _suppressErrors: Boolean = false;
		private var _iqCount: int = 0;
		private var _meetingUserName: String;
		private var _meetingID: String;
		private var _meetingNo: String; //_meetingID中@之前的部分
		private var _meetingPassword: String;
		private var _slaveFS: String;
		private var _lastGUID: String;
		private var _myJID: String;
		private var _myMMID: String;
		private var _audioController: AudioControl;
		private var _fmsIP: String;
		
		public function get meetingDomain(): String
		{
			return _connectionManager.connection.domain;
		}
		
		public function get meetingUserName(): String
		{
			return _meetingUserName;
		}        
		
		public function set meetingUserName(aValue: String): void
		{
			_meetingUserName = aValue;
		}
		
		public function get meetingID(): String
		{
			return _meetingID;
		}        
		
		public function set meetingID(aValue: String): void
		{
			_meetingID = aValue;
			var nIndex: int =_meetingID.indexOf("@");
			_meetingNo = _meetingID.substring(0, nIndex);
		}
	
		public function get meetingPassword(): String
		{
			return _meetingPassword;
		}        
		
		public function set meetingPassword(aValue: String): void
		{
			_meetingPassword = aValue;
		}
		
		public function get slaveFS(): String
		{
			//621174{621174}@slavefs_1.machine1.com
			var s: String = "{0}{{0}}@{1}";
			return mx.utils.StringUtil.substitute(s, _meetingNo, _slaveFS)
		}        
		
		public function set slaveFS(aValue: String): void
		{
			_slaveFS = aValue;
		}
		
		public function get lastGUID(): String
		{
			return _lastGUID;
		}        
		
		public function set lastGUID(aValue: String): void
		{
			_lastGUID = aValue;
		}
		
		public function get myJID(): String
		{
			return _myJID;
		}

        public function set myJID(aValue: String): void
        {
        	_myJID = aValue;
        }
        
        public function get fmsIP(): String
        {
        	return _fmsIP;
        }
        
        public function set fmsIP(aValue: String): void
        {
        	_fmsIP = aValue;
        }
        
        public function get myMMID(): String
        {
        	return _myMMID;
        }
        
        public function set myMMID(aValue: String): void
        {
        	_myMMID = aValue;
        }		
        	
		
	}	
}